using System;
using System.Collections.Generic;
using System.IO;

namespace PeDALS.Tools.AZ.PSTArchive.PSTData
{
    internal class PSTHeap
    {
        private byte mbBlockSignature;
        private byte mbClientSignature;
        private UInt32 m32HIDrawUserRoot;
        private SortedList<UInt32, byte[]> mslbaAllocation = new SortedList<uint, byte[]>();
        private SortedList<UInt32, PropertyValue> mslpvProperties = new SortedList<UInt32, PropertyValue>();
        private SortedList<UInt32, SortedList<UInt32, PropertyValue>> mslslpvTable = new SortedList<UInt32, SortedList<UInt32, PropertyValue>>();

        internal PSTHeap(List<byte[]> lbaBlock)
        {
            short shtPageMapOffset;
            UInt32[] a32FillLevel;
            short shtAllocationCount;
            short shtFreeCount;
            UInt32 u32Block = 0;

            foreach (byte[] baBlock in lbaBlock)
            {
                try
                {
                    List<short> lshtAllocationAddress = new List<short>();
                    MemoryStream msBlock = new MemoryStream(baBlock);
                    BinaryReader brReader = new BinaryReader(msBlock);

                    shtPageMapOffset = brReader.ReadInt16();
                    if (u32Block == 0)
                    {
                        mbBlockSignature = brReader.ReadByte();
                        mbClientSignature = brReader.ReadByte();
                        m32HIDrawUserRoot = brReader.ReadUInt32();
                        a32FillLevel = new UInt32[1];
                        a32FillLevel[0] = brReader.ReadUInt32();
                    }
                    if (((u32Block - 8) % 128) == 0)
                    {
                        a32FillLevel = new UInt32[16];
                        int intI = 0;
                        do
                        {
                            a32FillLevel[intI] = brReader.ReadUInt32();
                            intI++;
                        } while (intI < a32FillLevel.Length);
                    }
                    brReader.BaseStream.Seek(Convert.ToInt64(shtPageMapOffset), SeekOrigin.Begin);
                    shtAllocationCount = brReader.ReadInt16();
                    shtFreeCount = brReader.ReadInt16();
                    if (shtAllocationCount > 0)
                    {
                        do
                        {
                            lshtAllocationAddress.Add(brReader.ReadInt16());
                        } while (lshtAllocationAddress.Count < shtAllocationCount + 1);
                    }
                    if (shtAllocationCount > 0)
                    {
                        int intI = 0;
                        int intStartingBlockAllocationCount = mslbaAllocation.Count;
                        do
                        {
                            brReader.BaseStream.Seek(Convert.ToInt64(lshtAllocationAddress[intI]), SeekOrigin.Begin);
                            mslbaAllocation.Add(Convert.ToUInt32(((u32Block << 11) + intI + 1) << 5), brReader.ReadBytes(Convert.ToInt32(lshtAllocationAddress[intI + 1] - lshtAllocationAddress[intI])));
                            intI++;
                        } while (mslbaAllocation.Count - intStartingBlockAllocationCount < lshtAllocationAddress.Count - 1);
                    }
                    u32Block++;
                }
                catch (Exception eException)
                {
                    System.Diagnostics.Trace.Write(string.Format("Exception here, stopping to see what the error is"));
                }
            }
        }

        internal ContextType Context
        {
            get { return (ContextType)mbClientSignature; }
        }

        internal SortedList<UInt32, PropertyValue> Properties
        {
            get { return mslpvProperties; }
        }

        internal SortedList<UInt32, SortedList<UInt32, PropertyValue>> Table
        {
            get { return mslslpvTable; }
        }

        internal void ProcessHeap(UInt64 u64NIDraw, UInt32 u32HIDraw, SortedList<UInt32, byte[]> slbaSubNode, bool blnUnicode)
        {
            foreach (KeyValuePair<UInt32, byte[]> kvpEntry in slbaSubNode)
            {
                if (!mslbaAllocation.ContainsKey(kvpEntry.Key))
                {
                    mslbaAllocation.Add(kvpEntry.Key, kvpEntry.Value);
                }
                else
                {
                    System.Diagnostics.Trace.Write(string.Format("*******NID 0x{0:x8} ERROR:  Allocation HNID 0x{1:x8} Exists for Heap 0x{2:x8}", u64NIDraw, kvpEntry.Key, u32HIDraw));
                }
            }
            MemoryStream msEntry = null;
            BinaryReader brEntryReader = null;

            if (mslbaAllocation.ContainsKey(m32HIDrawUserRoot))
            {
                msEntry = new MemoryStream(mslbaAllocation[m32HIDrawUserRoot]);
                brEntryReader = new BinaryReader(msEntry);
                byte bType;
                switch (mbClientSignature)
                {
                    case 0x7c: // Table Context
                        bType = brEntryReader.ReadByte();
                        byte bColumnCount = brEntryReader.ReadByte();
                        List<UInt16> lu16DataOffsets = new List<UInt16>();
                        for (int intI = 0; intI < 4; intI++)
                        {
                            lu16DataOffsets.Add(brEntryReader.ReadUInt16());
                        }
                        UInt32 u32RowIndexHIDraw = brEntryReader.ReadUInt32();
                        UInt32 u32RowMatrixHIDraw = brEntryReader.ReadUInt32();
                        UInt32 u32Index = brEntryReader.ReadUInt32();
                        if (u32Index != 0)
                        {
                            System.Diagnostics.Trace.Write(string.Format("*******NID 0x{0:x8} Table Context Depricated Index 0x{1:X4} CLIENT SIGNATURE 0x{2:X2}*********", u64NIDraw, u32Index, mbClientSignature));
                        }
                        SortedList<UInt32, TCColumnDescription> sltccdColumns = new SortedList<UInt32, TCColumnDescription>();
                        do
                        {
                            TCColumnDescription tccdColumn = new TCColumnDescription(brEntryReader.ReadUInt32(), brEntryReader.ReadUInt16(), brEntryReader.ReadByte(), brEntryReader.ReadByte());
                            if (!sltccdColumns.ContainsKey(tccdColumn.DataOffset))
                            {
                                sltccdColumns.Add(tccdColumn.DataOffset, tccdColumn);
                            }
                            else
                            {
                                System.Diagnostics.Trace.Write(string.Format("*******NID 0x{0:x8} Invalid Column Offset PropertyTag 0x{1x8} 0x{2:x4}*********", u64NIDraw, tccdColumn.Tag, tccdColumn.DataOffset));
                            }
                        } while (sltccdColumns.Count < bColumnCount);
                        SortedList<UInt32, UInt64> slRowIndex = new SortedList<UInt32, UInt64>();
                        if (mslbaAllocation.ContainsKey(u32RowIndexHIDraw))
                        {
                            PSTBTH pbRowIndex = new PSTBTH(u32RowIndexHIDraw, mslbaAllocation);
                            foreach (PSTBTHLeaf pblIndex in pbRowIndex.BTHLeaves)
                            {
                                MemoryStream msKey = new MemoryStream(pblIndex.Key);
                                BinaryReader brKeyReader = new BinaryReader(msKey);
                                UInt64 u64NIDrawIndex = Convert.ToUInt64(brKeyReader.ReadUInt32());
                                if (msKey != null)
                                {
                                    msKey.Flush();
                                    msKey.Close();
                                }
                                msKey = null;
                                if (brKeyReader != null)
                                {
                                    brKeyReader.Close();
                                }
                                brKeyReader = null;
                                MemoryStream msIndex = new MemoryStream(pblIndex.Value);
                                BinaryReader brIndexReader = new BinaryReader(msIndex);
                                UInt32 u32RowIndex = blnUnicode ? brIndexReader.ReadUInt32() : Convert.ToUInt32(brIndexReader.ReadUInt16());
                                if (msIndex != null)
                                {
                                    msIndex.Flush();
                                    msIndex.Close();
                                }
                                msIndex = null;
                                if (brIndexReader != null)
                                {
                                    brIndexReader.Close();
                                }
                                brIndexReader = null;
                                slRowIndex.Add(u32RowIndex, u64NIDrawIndex);
                            }
                            pbRowIndex = null;
                        }
                        if (mslbaAllocation.ContainsKey(u32RowMatrixHIDraw))
                        {
                            MemoryStream msTableRow = null;
                            BinaryReader brTableRowReader = null;
                            msTableRow = new MemoryStream(mslbaAllocation[u32RowMatrixHIDraw]);
                            brTableRowReader = new BinaryReader(msTableRow);
                            foreach (KeyValuePair<UInt32, UInt64> kvpValue in slRowIndex)
                            {
                                brTableRowReader.BaseStream.Seek(RelativeJump(blnUnicode, brTableRowReader.BaseStream.Position, Convert.ToInt64(lu16DataOffsets[lu16DataOffsets.Count - 1])), SeekOrigin.Current);
                                SortedList<UInt32, PropertyValue> ltpLoadProperties = new SortedList<UInt32, PropertyValue>();
                                foreach (TCColumnDescription tccdColumn in sltccdColumns.Values)
                                {
                                    byte[] baData = brTableRowReader.ReadBytes(tccdColumn.DataSize);
                                    ltpLoadProperties.Add(tccdColumn.Tag, LoadProperty(u64NIDraw, u32HIDraw, Convert.ToUInt16(tccdColumn.Tag >> 16), Convert.ToUInt16(tccdColumn.Tag & 0xffff), baData));
                                }
                                byte[] baCellExistenceBlock = brTableRowReader.ReadBytes((sltccdColumns.Count / 8) + Convert.ToInt32(!(sltccdColumns.Count % 8 == 0)));
                                mslslpvTable.Add(kvpValue.Key, ltpLoadProperties);
                            }
                            int intI = 0;
                            intI++;
                        }
                        mslbaAllocation = null;
                        break;
                    case 0x9c:
                        System.Diagnostics.Trace.Write(string.Format("*******NID 0x{0:x8}:  Heap 0x{1:x8} RESERVED CLIENT SIGNATURE 0x{2:X2}; Data Disposition TBD*********", u64NIDraw, u32HIDraw, mbClientSignature));
                        break;
                    case 0xa5:
                        System.Diagnostics.Trace.Write(string.Format("*******NID 0x{0:x8}:  Heap 0x{1:x8} RESERVED CLIENT SIGNATURE 0x{2:X2}; Data Disposition TBD*********", u64NIDraw, u32HIDraw, mbClientSignature));
                        break;
                    case 0xac:
                        System.Diagnostics.Trace.Write(string.Format("*******NID 0x{0:x8}:  Heap 0x{1:x8} RESERVED CLIENT SIGNATURE 0x{2:X2}; Data Disposition TBD*********", u64NIDraw, u32HIDraw, mbClientSignature));
                        break;
                    case 0xb5:
                        System.Diagnostics.Trace.Write(string.Format("*******NID 0x{0:x8}:  Heap 0x{1:x8} BTree on Heap CLIENT SIGNATURE 0x{2:X2}*********", u64NIDraw, u32HIDraw, mbClientSignature));
                        PSTBTH pbBTHNode = new PSTBTH(m32HIDrawUserRoot, mslbaAllocation);
                        foreach (PSTBTHLeaf pblNode in pbBTHNode.BTHLeaves)
                        {
                        }
                        pbBTHNode = null;

                        break;
                    case 0xbc:
                        PSTBTH pbBTHProperties = new PSTBTH(m32HIDrawUserRoot, mslbaAllocation);
                        foreach (PSTBTHLeaf pblLeaf in pbBTHProperties.BTHLeaves)
                        {
                            MemoryStream msTag = new MemoryStream(pblLeaf.Key);
                            BinaryReader brTagReader = new BinaryReader(msTag);
                            UInt16 u16Tag = brTagReader.ReadUInt16();
                            if (msTag != null)
                            {
                                msTag.Flush();
                                msTag.Close();
                            }
                            msTag = null;
                            if (brTagReader != null)
                            {
                                brTagReader.Close();
                            }
                            brTagReader = null;
                            MemoryStream msValue = new MemoryStream(pblLeaf.Value);
                            BinaryReader brValueReader = new BinaryReader(msValue);
                            UInt16 u16Type = brValueReader.ReadUInt16();
                            byte[] baData = brValueReader.ReadBytes(4);
                            if (msValue != null)
                            {
                                msValue.Flush();
                                msValue.Close();
                            }
                            msValue = null;
                            if (brValueReader != null)
                            {
                                brValueReader.Close();
                            }
                            brValueReader = null;
                            mslpvProperties.Add(Convert.ToUInt32(u16Tag) << 16 | Convert.ToUInt32(u16Type), LoadProperty(u64NIDraw, u32HIDraw, u16Tag, u16Type, baData));
                        }
                        pbBTHNode = null;
                        mslbaAllocation = null;
                        break;

                    default:
                        System.Diagnostics.Trace.Write(string.Format("*******NID 0x{0:x8}:  Heap 0x{1:x8} UNKNOWN RESERVED CLIENT SIGNATURE 0x{2:X2}*********", u64NIDraw, u32HIDraw, mbClientSignature));
                        break;
                }
            }
            else
            {
                System.Diagnostics.Trace.Write(string.Format("*******NID 0x{0:x8} PSTHEAP ERROR:  Heap 0x{1:x8} HEAPROOT HID 0x{2:x8} NOT FOUND*********", u64NIDraw, u32HIDraw, m32HIDrawUserRoot));
                List<UInt32> lu32HIDraw = new List<UInt32>(mslbaAllocation.Keys);
                List<UInt32> lu32HIDrawFiltered = lu32HIDraw.FindAll(delegate(UInt32 u32FoundEntry) { return (u32FoundEntry & 0x1f) > 0; });
                List<byte[]> lbaData = new List<byte[]>(mslbaAllocation.Values);
                List<byte[]> lbaDataFiltered = lbaData.FindAll(delegate(byte[] baFoundEntry) { return baFoundEntry.Length > 0; });
                List<byte[]> lbaDataFiltered2 = lbaDataFiltered.FindAll(delegate(byte[] baFoundEntry) { return baFoundEntry[0] == 0x7c; });
            }
        }

        private PropertyValue LoadProperty(UInt64 u64NIDraw, UInt32 u32HIDraw, UInt16 u16Tag, UInt16 u16Type, UInt64 u64Value)
        {
            PropertyValue tpReturnProperty = null;
            try
            {
                if (Convert.ToBoolean(u16Type & 0x1000))
                {
                    List<object> loValues = new List<object>();

                    //MemoryStream msBlock = new MemoryStream(baBlock);
                    //BinaryReader brReader = new BinaryReader(msBlock);

                    //loValues.Add(GetPropertyValue(Convert.ToUInt16(u16Type & 0xFFF), u64Value));

                    tpReturnProperty = new PropertyValue(u16Tag, u16Type, loValues);
                }
                else
                {
                    tpReturnProperty = new PropertyValue(u16Tag, u16Type, GetPropertyValue(Convert.ToUInt16(u16Type & 0xFFF), u64Value));
                }
            }
            catch (global::System.Exception eException)
            {
                UInt32 u32Value = u64Value <= UInt32.MaxValue ? Convert.ToUInt32(u64Value) : 0;
                System.Diagnostics.Trace.Write(string.Format("*******NID 0x{0:x8} Heap 0x{1:x8} Invalid Property Conversion {2}; PropertyTag 0x{3:x4}{4:x4} Value 0x{5:x16}/0x{6:x8} {7}*********", u64NIDraw, u32HIDraw, eException.Message, u16Tag, u16Type, u64Value, u32Value, mslbaAllocation.ContainsKey(u32Value) ? BitConverter.ToString(mslbaAllocation[u32Value]) : "Block Doesn't Exist"));
            }
            return tpReturnProperty;
        }

        private object GetPropertyValue(UInt16 u16Type, UInt64 u64Value)
        {
            UInt32 u32Value = 0;
            System.Text.UTF8Encoding aeEncoder = new System.Text.UTF8Encoding();
            System.Text.UnicodeEncoding ueEncoder = new System.Text.UnicodeEncoding();
            object oReturnValue = null;
            try
            {
                u32Value = u64Value <= UInt32.MaxValue ? Convert.ToUInt32(u64Value) : 0;
                switch (u16Type)
                {
                    case 0x0000:
                        oReturnValue = string.Format("UNSPECIFIED - {0}{1}", mslbaAllocation.ContainsKey(u32Value) ? "" : "HNID Not Found - ", BitConverter.ToString(mslbaAllocation.ContainsKey(u32Value) ? mslbaAllocation[u32Value] : BitConverter.GetBytes(u32Value)));
                        break;
                    case 0x0001:
                        oReturnValue = string.Format("NULL - {0}{1}", mslbaAllocation.ContainsKey(u32Value) ? "" : "HNID Not Found - ", BitConverter.ToString(mslbaAllocation.ContainsKey(u32Value) ? mslbaAllocation[u32Value] : BitConverter.GetBytes(u32Value)));
                        break;
                    case 0x0002:
                        oReturnValue = Convert.ToUInt16(u32Value);
                        break;
                    case 0x0003:
                        oReturnValue = Convert.ToUInt32(u32Value);
                        break;
                    case 0x0004:
                        oReturnValue = Convert.ToSingle(u32Value);
                        break;
                    case 0x0005:
                        oReturnValue = mbClientSignature == 0x7c ? Convert.ToDouble(u64Value) : BitConverter.ToDouble(mslbaAllocation.ContainsKey(u32Value) ? mslbaAllocation[u32Value] : BitConverter.GetBytes(u32Value), 0);
                        break;
                    case 0x0006:
                        oReturnValue = mbClientSignature == 0x7c ? string.Format("Currency - {0}", u64Value) : string.Format("Currency - {0}{1}", mslbaAllocation.ContainsKey(u32Value) ? "" : "HNID Not Found - ", BitConverter.ToDouble(mslbaAllocation.ContainsKey(u32Value) ? mslbaAllocation[u32Value] : BitConverter.GetBytes(u32Value), 0));
                        break;
                    case 0x0007:
                        oReturnValue = mbClientSignature == 0x7c ? string.Format("AppTime - {0}", Convert.ToDouble(u64Value)) : string.Format("AppTime - {0}{1}", mslbaAllocation.ContainsKey(u32Value) ? "" : "HNID Not Found - ", BitConverter.ToDouble(mslbaAllocation.ContainsKey(u32Value) ? mslbaAllocation[u32Value] : BitConverter.GetBytes(u32Value), 0));
                        break;
                    case 0x000A:
                        oReturnValue = string.Format("ErrorCode - {0}", Convert.ToUInt32(u32Value));
                        break;
                    case 0x000B:
                        oReturnValue = Convert.ToBoolean(u32Value);
                        break;
                    case 0x000D:
                        oReturnValue = mslbaAllocation.ContainsKey(u32Value) ? mslbaAllocation[u32Value] : u32Value != 0 ? BitConverter.GetBytes(u32Value) : new byte[0];
                        break;
                    case 0x0014:
                        oReturnValue = mbClientSignature == 0x7c ? u64Value : BitConverter.ToUInt64(mslbaAllocation.ContainsKey(u32Value) ? mslbaAllocation[u32Value] : BitConverter.GetBytes(u32Value), 0);
                        break;
                    case 0x001E:
                        oReturnValue = aeEncoder.GetString(mslbaAllocation.ContainsKey(u32Value) ? mslbaAllocation[u32Value] : new byte[0]);
                        break;
                    case 0x001F:
                        oReturnValue = ueEncoder.GetString(mslbaAllocation.ContainsKey(u32Value) ? mslbaAllocation[u32Value] : new byte[0]);
                        break;
                    case 0x0040:
                        oReturnValue = DateTime.FromFileTime(mbClientSignature == 0x7c ? Convert.ToInt64(u64Value) : BitConverter.ToInt64(mslbaAllocation.ContainsKey(u32Value) ? mslbaAllocation[u32Value] : u32Value != 0 ? BitConverter.GetBytes(u32Value) : BitConverter.GetBytes(u64Value), 0));
                        break;
                    case 0x0048:
                        oReturnValue = string.Format("Class ID GUID - {0}{1}", mslbaAllocation.ContainsKey(u32Value) ? "" : "HNID Not Found - ", BitConverter.ToString(mslbaAllocation.ContainsKey(u32Value) ? mslbaAllocation[u32Value] : BitConverter.GetBytes(u32Value)));
                        break;
                    case 0x00FB:
                        oReturnValue = string.Format("SVREID - {0}{1}", mslbaAllocation.ContainsKey(u32Value) ? "" : "HNID Not Found - ", BitConverter.ToString(mslbaAllocation.ContainsKey(u32Value) ? mslbaAllocation[u32Value] : BitConverter.GetBytes(u32Value)));
                        break;
                    case 0x00FD:
                        oReturnValue = string.Format("SRESTRICT - {0}{1}", mslbaAllocation.ContainsKey(u32Value) ? "" : "HNID Not Found - ", BitConverter.ToString(mslbaAllocation.ContainsKey(u32Value) ? mslbaAllocation[u32Value] : BitConverter.GetBytes(u32Value)));
                        break;
                    case 0x00FE:
                        oReturnValue = string.Format("ACTIONS - {0}{1}", mslbaAllocation.ContainsKey(u32Value) ? "" : "HNID Not Found - ", BitConverter.ToString(mslbaAllocation.ContainsKey(u32Value) ? mslbaAllocation[u32Value] : BitConverter.GetBytes(u32Value)));
                        break;
                    case 0x0102:
                        oReturnValue = mslbaAllocation.ContainsKey(u32Value) ? mslbaAllocation[u32Value] : BitConverter.GetBytes(u32Value);
                        break;
                    default:
                        oReturnValue = string.Format("UNKNOWN - 0x{0:X4} - {1}{2}", u16Type, mslbaAllocation.ContainsKey(u32Value) ? "" : "HNID Not Found - ", BitConverter.ToString(mslbaAllocation.ContainsKey(u32Value) ? mslbaAllocation[u32Value] : BitConverter.GetBytes(u32Value)));
                        break;
                }
            }
            catch (global::System.Exception eException)
            {
            }
            return oReturnValue;
        }

        private PropertyValue LoadProperty(UInt64 u64NIDraw, UInt32 u32HIDraw, UInt16 u16Tag, UInt16 u16Type, byte[] baInput)
        {
            byte[] baValue = null;
            int intTypeLength = 0;
            PropertyValue tpReturnProperty = null;
            try
            {
                switch (u16Type)
                {
                    case 0x0001:
                    case 0x0002:
                    case 0x0003:
                    case 0x0004:
                    case 0x000A:
                    case 0x000B:
                        baValue = baInput;
                        break;
                    case 0x0005:
                    case 0x0006:
                    case 0x0007:
                    case 0x0014:
                    case 0x0040:
                        baValue = mbClientSignature == 0x7c ? baInput : mslbaAllocation.ContainsKey(baInput.Length > 0 ? BitConverter.ToUInt32(baInput, 0) : 0) ? mslbaAllocation[BitConverter.ToUInt32(baInput, 0)] : baInput.Length > 0 ? BitConverter.ToUInt32(baInput, 0) != 0 ? baInput : new byte[0] : new byte[0];
                        break;
                    default:
                        baValue = mslbaAllocation.ContainsKey(baInput.Length > 0 ? BitConverter.ToUInt32(baInput, 0) : 0) ? mslbaAllocation[BitConverter.ToUInt32(baInput, 0)] : baInput.Length > 0 ? BitConverter.ToUInt32(baInput, 0) != 0 ? baInput : new byte[0] : new byte[0];
                        break;
                }
                if (Convert.ToBoolean(u16Type & 0x1000))
                {
                    List<object> loValues = new List<object>();
                    if (baValue.Length > 0)
                    {
                        List<UInt32> lu32ValueAddress = new List<UInt32>();
                        List<byte[]> lbaValues = new List<byte[]>();
                        MemoryStream msBlock = new MemoryStream(baValue);
                        BinaryReader brReader = new BinaryReader(msBlock);
                        switch (Convert.ToUInt16(u16Type & 0xFFF))
                        {
                            case 0x000B:
                                intTypeLength = 1;
                                break;
                            case 0x0002:
                                intTypeLength = 2;
                                break;
                            case 0x0003:
                            case 0x0004:
                            case 0x000A:
                                intTypeLength = 4;
                                break;
                            case 0x0005:
                            case 0x0006:
                            case 0x0007:
                            case 0x0014:
                            case 0x0040:
                                intTypeLength = 8;
                                break;
                            case 0x0048:
                                intTypeLength = 16;
                                break;
                            default:
                                break;
                        }
                        if (intTypeLength > 0)
                        {
                            int intValueCount = (baValue.Length / intTypeLength);
                            do
                            {
                                lbaValues.Add(brReader.ReadBytes(intTypeLength));
                            } while (lbaValues.Count < intValueCount);
                        }
                        else
                        {
                            UInt32 u32ValueCount = brReader.ReadUInt32();
                            if (u32ValueCount > 0)
                            {
                                do
                                {
                                    UInt32 u32Temp = brReader.ReadUInt32();
                                    lu32ValueAddress.Add(u32Temp > 0 ? u32Temp : (UInt32)baValue.Length);
                                } while (lu32ValueAddress.Count < u32ValueCount + 1);
                            }
                            if (u32ValueCount > 0)
                            {
                                int intI = 0;
                                do
                                {
                                    brReader.BaseStream.Seek(Convert.ToInt32(lu32ValueAddress[intI]), SeekOrigin.Begin);
                                    lbaValues.Add(brReader.ReadBytes(Convert.ToInt32(lu32ValueAddress[intI + 1] - lu32ValueAddress[intI])));
                                    intI++;
                                } while (lbaValues.Count < lu32ValueAddress.Count - 1);
                            }
                        }

                        foreach (byte[] baIndividualValue in lbaValues)
                        {
                            loValues.Add(GetPropertyValue(Convert.ToUInt16(u16Type & 0xFFF), baIndividualValue));
                        }
                    }
                    tpReturnProperty = new PropertyValue(u16Tag, u16Type, loValues);
                }
                else
                {
                    tpReturnProperty = new PropertyValue(u16Tag, u16Type, GetPropertyValue(Convert.ToUInt16(u16Type & 0xFFF), baValue));
                }
            }
            catch (global::System.Exception eException)
            {
            }
            return tpReturnProperty;
        }

        private object GetPropertyValue(UInt16 u16Type, byte[] baValue)
        {
            System.Text.UTF8Encoding aeEncoder = new System.Text.UTF8Encoding();
            System.Text.UnicodeEncoding ueEncoder = new System.Text.UnicodeEncoding();
            object oReturnValue = new byte[0];
            try
            {
                switch (u16Type)
                {
                    case 0x0000:
                        oReturnValue = string.Format("UNSPECIFIED - {0}{1}", baValue.Length > 0 ? BitConverter.ToString(baValue) : "Empty Value", string.Format(" - VARFLAG 0x{0:X4}", u16Type));
                        break;
                    case 0x0001:
                        oReturnValue = string.Format("NULL - {0}{1}", baValue.Length > 0 ? BitConverter.ToString(baValue) : "Empty Value", string.Format(" - VARFLAG 0x{0:X4}", u16Type));
                        break;
                    case 0x0002:
                        oReturnValue = baValue.Length > 0 ? BitConverter.ToUInt16(baValue, 0) : (UInt16) 0;
                        break;
                    case 0x0003:
                        oReturnValue = baValue.Length > 0 ? BitConverter.ToUInt32(baValue, 0) : 0;
                        break;
                    case 0x0004:
                        oReturnValue = baValue.Length > 0 ? BitConverter.ToSingle(baValue, 0) : 0;
                        break;
                    case 0x0005:
                        oReturnValue = baValue.Length > 0 ? BitConverter.ToDouble(baValue, 0) : 0;
                        break;
                    case 0x0006:
                        oReturnValue = string.Format("Currency - {0:C}{1}", baValue.Length > 0 ? Convert.ToDecimal(BitConverter.ToInt64(baValue, 0)) / 10000 : 0, string.Format(" - VARFLAG 0x{0:X4}", u16Type));
                        break;
                    case 0x0007:
                        if (baValue.Length > 0)
                        {
                            if (BitConverter.ToDouble(baValue, 0) > 0)
                            {
                                oReturnValue = DateTime.FromOADate(BitConverter.ToDouble(baValue, 0));
                            }
                            else
                            {
                                oReturnValue = string.Format("Apptime - {0}{1}", DateTime.FromOADate(baValue.Length > 0 ? BitConverter.ToDouble(baValue, 0) : 0), string.Format(" - VARFLAG 0x{0:X4}", u16Type));
                            }
                        }
                        break;
                    case 0x000A:
                        oReturnValue = string.Format("ErrorCode - 0x{0:x8}{1}", baValue.Length > 0 ? BitConverter.ToUInt32(baValue, 0) : 0, string.Format(" - VARFLAG 0x{0:X4}", u16Type));
                        break;
                    case 0x000B:
                        oReturnValue = baValue.Length > 0 ? BitConverter.ToBoolean(baValue, 0) : false;
                        break;
                    case 0x000D:
                        oReturnValue = string.Format("COM Object Binary - {0}{1}", baValue.Length > 0 ? BitConverter.ToString(baValue) : "Empty Value", string.Format(" - VARFLAG 0x{0:X4}", u16Type));
                        break;
                    case 0x0014:
                        oReturnValue = baValue.Length > 0 ? BitConverter.ToUInt64(baValue, 0) : 0;
                        break;
                    case 0x001E:
                        oReturnValue = aeEncoder.GetString(baValue);
                        break;
                    case 0x001F:
                        oReturnValue = ueEncoder.GetString(baValue);
                        break;
                    case 0x0040:
                        long lngValue = 0;
                        if (baValue.Length > 0)
                        {
                            if (BitConverter.ToInt64(baValue, 0) > 0)
                            {
                                lngValue = BitConverter.ToInt64(baValue, 0);
                            }
                        }
                        oReturnValue = DateTime.FromFileTime(lngValue);
                        break;
                    case 0x0048:
                        oReturnValue = string.Format("Class ID GUID - {0}{1}", baValue.Length > 0 ? BitConverter.ToString(baValue) : "Empty Value", string.Format(" - VARFLAG 0x{0:X4}", u16Type));
                        break;
                    case 0x00FB:
                        oReturnValue = string.Format("SVREID - {0}{1}", baValue.Length > 0 ? BitConverter.ToString(baValue) : "Empty Value", string.Format(" - VARFLAG 0x{0:X4}", u16Type));
                        break;
                    case 0x00FD:
                        oReturnValue = string.Format("SRESTRICT - {0}{1}", baValue.Length > 0 ? BitConverter.ToString(baValue) : "Empty Value", string.Format(" - VARFLAG 0x{0:X4}", u16Type));
                        break;
                    case 0x00FE:
                        oReturnValue = string.Format("ACTIONS - {0}{1}", baValue.Length > 0 ? BitConverter.ToString(baValue) : "Empty Value", string.Format(" - VARFLAG 0x{0:X4}", u16Type));
                        break;
                    case 0x0102:
                        oReturnValue = baValue;
                        break;
                    default:
                        oReturnValue = string.Format("UNKNOWN - {0}{1}", baValue.Length > 0 ? BitConverter.ToString(baValue) : "Empty Value", string.Format(" - VARFLAG 0x{0:X4}", u16Type));
                        break;
                }
            }
            catch (global::System.Exception eException)
            {
            }
            return oReturnValue;
        }

        private long RelativeJump(bool blnUnicode, long lngCurrentPosition, long lngEntryLength)
        {
            long lngBlockRemainder = (lngCurrentPosition + lngEntryLength) % Convert.ToInt64(0x1FF0 + (blnUnicode ? 0 : 4));
            if (lngBlockRemainder < lngEntryLength)
            {
                return lngEntryLength - lngBlockRemainder;
            }
            else
            {
                return (long)0;
            }
        }
    }
}